<?php
/**
 * @desc request class file
 * @author nana
 * @date 2011
 *
 */
class NICED_Request extends NICED_Container
{
	protected static $instance;
    protected static $loaded = false;
    private $mRequestName = NULL;
	private $mUri;
    private static $router = array();

	protected static $mRequestNameSeparator = '.';

	public static function getInstance() 
	{
		if (!isset(self::$instance) || !is_object(self::$instance)) 
		{
			self::$instance = new NICED_Request();
		}

		return self::$instance;
	}
	
	public function __construct($request_name  = NULL) 
	{
        self::$router = LC::get('router');
        if (!$request_name)
        {
            $request_name = $this->resolveReqeustNameFromURI();
        }
        else
        {
            $request_name = strtolower($request_name);
        }

        $this->mRequestName = $this->filterRequestName($request_name);
        
        $bool = $this->checkAppEnable($this->mRequestName);

        if (!$bool)
        {
            throw new NICED_Exception("app ".$this->mRequestName." is disable");
        }
	}

    /**
     * check request name, if not valid, use default in board_config
     */
    private static function filterRequestName($request_name)
    {
		$slice = self::parseRequestName($request_name);

		if (!$slice || count($slice) < 2) //get default value in board_config
		{ 
			$default_request_name = SC::get('board_config.default_request_name');
			$default_slice        = self::parseRequestName($default_request_name);
			
			$slice = array_merge($slice, array_slice($default_slice, count($slice)));
			
			$request_name = self::generateRequestName($slice);

			if (!in_array($request_name, self::$router))
			{
				throw new NICED_Exception("default app and default action not in router");
			}
		}

        return $request_name;
    }

    private function resolveReqeustNameFromURI()
    {
        $request_name = NULL;
		$uri      = $this->getURI();
		$base_url = NICED_URL::getBaseURL(false, true);//http://host/index.php

        //if base_url is not null
		if (trim($base_url, '/'))
		{
            //if base_url in uri
			if (false !== strpos($uri, rtrim($base_url, '/'))) 
			{
				$start = strpos($base_url, rtrim($uri)) + strlen($base_url);
                //strip base_url from uri
				$uri = substr($uri, $start);//if url is http://host/hello/user, $uri=hello/user
			}
			else 
			{
				throw new NICED_Exception('uri parse error');
			}
		}

		$strip_chars      = "/\n\r\0\t\x0B ";
		$url_args         = array();
		list($url_path, ) = explode('?', $uri, 2);//remove get param
		$url_path         = trim($url_path, '/');

        //if not in router_table, set url_path to args, for loadArg
		while (strlen($url_path) != 0 && !(isset(self::$router[$url_path]))) 
		{
            $new_arg = substr($url_path, strrpos($url_path, '/'));
            $url_path = substr($url_path, 0, strrpos($url_path, $new_arg));
            array_unshift($url_args, trim($new_arg, $strip_chars));
		}

		$this->set('__args__', $url_args);

		if ($url_path != '') 
		{
			$request_name = strtolower(self::$router[$url_path]);
		} 

        $this->mRequestName = $request_name;

        return $request_name;
    }

    public static function checkAppEnable($request_name)
    {
        $apps = &LC::get("apps"); 

		$slice = self::parseRequestName($request_name);

        $name = "";
        $app_flag = false;

        foreach ($slice as $k => $v)
        {
            $name = $name ? $name.".".$v : $v;
            $flag = isset($apps[$name]) ? (int)$apps[$name] : NULL;

            if (1 === $flag)
            {
                $app_flag = true;

                if ($request_name !== $name)
                {
                    continue;
                }
                else
                {   
                    break;
                }
            }
            else if (0 === $flag)
            {
                $app_flag = false;
                if ($request_name === $name)
                {
                    break;
                }
                else
                {
                    continue;
                }
            }
            else
            {
                if ($request_name === $name)
                {
                    break;
                }
                else
                {
                    continue;
                }
            }
        }

        return $app_flag;
    }

    protected function getURI()
    {
        if ('cli' === PHP_SAPI)
        {
            $this->mUri = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : NULL;
            //remove GET param from uri
            if (($query = strpos($this->mUri, '?')) !== false)
            {
                list ($this->mUri, $query) = explode('?', $this->mUri, 2);
                // Parse the query string into $_GET
                parse_str($query, $_GET);
                parse_str($query, $_REQUEST);
            }
        }
        else
        {
            if (!isset($_SERVER["REDIRECT_STATUS"]) || 200 != $_SERVER["REDIRECT_STATUS"] || !isset($_SERVER['PATH_INFO']))
            {
                $this->mUri = $_SERVER['REQUEST_URI'];
            }
            else
            {
                $this->mUri = $_SERVER["PATH_INFO"];
            }
    }

    return $this->mUri;
}

	public function getRequestName()
	{
		return $this->mRequestName;
	}

	public static function generateRequestName($arr)
	{
		return implode(self::$mRequestNameSeparator, $arr);
	}

	public static function parseRequestName($request_name)
	{
		$slice = array();

		if ($request_name)
		{
			$slice = explode(self::$mRequestNameSeparator, $request_name);
		}

		return $slice;
	}

	public static function checkRequestName($request_name)
	{
		if(strlen($request_name) < 1)  
		{
			throw new NICED_ValidationException('invalid request name : '.$request_name);
		}

		if(!strpos($request_name, self::$mRequestNameSeparator)) 
		{
			throw new NICED_ValidationException('invalid request name: '.$request_name);
		}	

		return true;
	}

	public function loadArg($index, $key, $default = null, $filter = 'safe') 
	{
		$args  = $this->get('__args__');
		$index = (int)$index;

		if (isset($args[$index])) 
		{
			$value = $args[$index];
		} 
		else 
		{
			$value = $default;
		}

		if (!is_null($value)) 
		{
			$value = self::filterParam($value, $filter, $default);
		}

		$this->set($key, $value);
		$this->set("params.".$key, $value);

		return $value;
	}

	public function loadParam($key, $default = null, $filter = 'safe', $source = 'REQUEST', $trim = false) 
	{
		$value = self::loadParamFromVar($key, $default, $filter, $source, $trim);

		$this->set($key, $value);
		$this->set("params.".$key, $value);

		return $value;
	}

	public static function loadParamFromVar($key, $default = null, $filter = 'safe', $source = 'request', $trim = false)
	{
		switch (trim(strtoupper($source), "_"))
		{
			case 'POST':
			{
				$params = $_POST;
				break;
			}
			case 'GET':
			{
				$params = $_GET;
				break;
			}
			case 'COOKIE':
			{
				$params = $_COOKIE;
				break;
			}
			case 'SERVER':
			{
				$params = $_SERVER;
				break;
			}
			case 'ENV':
			{
				$params = $_ENV;
				break;
			}
			case 'FILE':
			{
			}
			case 'FILES':
			{
				$params = $_FILES;
				break;
			}
			default:
			{
				$params = $_REQUEST;
				break;
			}
		} //end switch
		
		if (isset($params[$key])) 
		{
			$value = $params[$key];
		} 
		else 
		{
			$value = $default;
		}

		if ($trim)
		{
			$value = trim($value);
		}

		$callback = NULL;
		if (is_array($filter))
		{
			$callback = isset($filter["callback"]) ? $filter["callback"] : NULL;
		}

		if ($callback)
		{
			foreach ($callback as $k)
			{
			    $value = call_user_func($k, $value);
			}
		}

		if (!is_null($value)) 
		{
			$value = self::filterParam($value, $filter, $default);
		}

		return $value;
	}

	public static function filterParam($value, $filter, $default_value) 
	{
		if(is_array($filter)) 
		{
			switch (key($filter)) 
			{
				case 'regex' :
				{
					$pattern = $filter['regex'];
					$filter = 'regex';
					break;
				}
				case 'enum' :
				{
					$pattern = $filter['enum'];

					if (!is_array($pattern))
					{
						$pattern = array($pattern);
					}

					$filter = 'enum';
					break;
				}
				default :
				{
				}
			}
		}
		
		switch ($filter) 
		{
			case 'alpha' :
			{
				return ctype_alpha($value) ? $value : $default_value;
			}
			case 'int' :
			{
				return (ctype_digit($value) || ($value[0] == '-' && ctype_digit(substr($value, 1)))) ? $value : $default_value;
			}
			case 'posint' :
			{
				return ctype_digit($value) ? $value : $default_value;
			}
			case 'numeric' :
			{
				return is_numeric($value) ? $value : $default_value;
			}
			case 'alphanum' :
			{
				return ctype_alnum($value) ? $value : $default_value;
			}
			case 'bool' :
			{
				return $value ? TRUE : FALSE;
			}
			case 'enum' :
			{
				return in_array($value, $pattern) ? $value : $default_value;
			}
			case 'regex' :
			{
				return preg_match($pattern, $value) ? $value : $default_value;
			}
			case 'raw' :
			{
				return $value;
			}
			case 'more_safe' :
			{
				$value = str_replace(array('<', '>', '"', "'", '{', '(', '&', '%'), '', $value);

				return ($value || 0 == $value) ? $value : $default_value;
			}
			case 'safe' :
			{
			}
			default :
			{
				$value = str_replace(array('<', '>', '"', "'", '{', '('), '', $value);//, '&', '%'

				return ($value || 0 == $value) ? $value : $default_value;// set to default value if there is nothing left after filtering
			}
		} //end switch
	}
} //end class
